package eva

import (
	"bytes"
	"io"
	"os"
	"testing"

	"gitcode.com/deyiyangyang/gascheme/obj"
)

// TestDisplayFunction tests the display function
func TestDisplayFunction(t *testing.T) {
	// Capture stdout
	old := os.Stdout
	r, w, _ := os.Pipe()
	os.Stdout = w

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Test display with a string
	str := &obj.String{Value: "hello"}
	result := Display([]obj.Object{str}, env)

	// Close the writer and restore stdout
	w.Close()
	os.Stdout = old

	// Read the captured output
	var buf bytes.Buffer
	io.Copy(&buf, r)
	output := buf.String()

	// Check that the output is correct
	expectedOutput := "hello"
	if output != expectedOutput {
		t.Errorf("display output is wrong. Got %q, want %q", output, expectedOutput)
	}

	// Check that the result is silent void (should not be printed)
	if result.Type() != obj.SILENT_VOID_OBJ {
		t.Errorf("display should return silent void, got %s", result.Type())
	}

	// Test display with an integer
	r2, w2, _ := os.Pipe()
	os.Stdout = w2

	integer := &obj.Integer{Value: 42}
	result2 := Display([]obj.Object{integer}, env)

	w2.Close()
	os.Stdout = old

	var buf2 bytes.Buffer
	io.Copy(&buf2, r2)
	output2 := buf2.String()

	expectedOutput2 := "42"
	if output2 != expectedOutput2 {
		t.Errorf("display output is wrong. Got %q, want %q", output2, expectedOutput2)
	}

	if result2.Type() != obj.SILENT_VOID_OBJ {
		t.Errorf("display should return silent void, got %s", result2.Type())
	}
}

// TestNewlineFunction tests the newline function
func TestNewlineFunction(t *testing.T) {
	// Capture stdout
	old := os.Stdout
	r, w, _ := os.Pipe()
	os.Stdout = w

	// Create an environment and register built-in procedures
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Test newline
	result := Newline([]obj.Object{}, env)

	// Close the writer and restore stdout
	w.Close()
	os.Stdout = old

	// Read the captured output
	var buf bytes.Buffer
	io.Copy(&buf, r)
	output := buf.String()

	// Check that the output is a newline
	if output != "\n" {
		t.Errorf("newline output is wrong. Got %q, want %q", output, "\n")
	}

	// Check that the result is silent void (should not be printed)
	if result.Type() != obj.SILENT_VOID_OBJ {
		t.Errorf("newline should return silent void, got %s", result.Type())
	}
}

// TestDisplayError tests error cases for display function
func TestDisplayError(t *testing.T) {
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Test with no arguments
	result1 := Display([]obj.Object{}, env)
	if result1.Type() != obj.ERROR_OBJ {
		t.Errorf("display with no arguments should return an error")
	}

	// Test with too many arguments
	result2 := Display([]obj.Object{
		&obj.String{Value: "hello"},
		&obj.String{Value: "world"},
	}, env)
	if result2.Type() != obj.ERROR_OBJ {
		t.Errorf("display with too many arguments should return an error")
	}
}

// TestNewlineError tests error cases for newline function
func TestNewlineError(t *testing.T) {
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)

	// Test with arguments
	result := Newline([]obj.Object{&obj.String{Value: "hello"}}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("newline with arguments should return an error")
	}
}